package com.cloxtec.scada.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.mongodb.BasicDBObject;
import com.mongodb.client.FindIterable;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;

@Slf4j
@Component
public class MongoHelper {

    @Resource
    private MongoTemplate mongoTemplate;


    public List<JSONObject> find(String collectionName) {
        List<JSONObject> jsonObjectList = new LinkedList<JSONObject>();
        try {
            BasicDBObject basicDBObject = new BasicDBObject();
            FindIterable<Document> findIterable = mongoTemplate.getCollection(collectionName).find(basicDBObject);
            Iterator<Document> iteratorSet = findIterable.iterator();
            while (iteratorSet.hasNext()) {
                Document next = iteratorSet.next();
                jsonObjectList.add(JSON.parseObject(JSON.toJSONString(next)));
                //jsonObjectList.add(JSON.parseObject(next.toJson()));
            }
        } catch (Exception e) {
            return null;
        }
        return jsonObjectList;
    }

    public List<JSONObject> find(String collectionName, Map<String, Object> criteria_params) {
        List<JSONObject> jsonObjectList = new LinkedList<JSONObject>();
        try {
            BasicDBObject basicDBObject = new BasicDBObject();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                basicDBObject.append(key, obj);
            }
            FindIterable<Document> findIterable = mongoTemplate.getCollection(collectionName).find(basicDBObject);
            Iterator<Document> iteratorSet = findIterable.iterator();
            while (iteratorSet.hasNext()) {
                Document next = iteratorSet.next();
                jsonObjectList.add(JSON.parseObject(JSON.toJSONString(next)));
            }
        } catch (Exception e) {
            return null;
        }
        return jsonObjectList;
    }

    public List<JSONObject> findData(String collectionName, Map<String, Object> criteria_params, JSONArray arrKeys) {
        List<JSONObject> jsonObjectList = new LinkedList<JSONObject>();
        try {
            BasicDBObject basicDBObject = new BasicDBObject();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                basicDBObject.append(key, obj);
            }

            BasicDBObject retKeys = new BasicDBObject();
            for (int i=0;i<arrKeys.size();i++){
                retKeys.append(arrKeys.getString(i), 1);
            }

            FindIterable<Document> findIterable = mongoTemplate.getCollection(collectionName).find(basicDBObject).projection(retKeys);
            Iterator<Document> iteratorSet = findIterable.iterator();
            while (iteratorSet.hasNext()) {
                Document next = iteratorSet.next();
                jsonObjectList.add(JSON.parseObject(JSON.toJSONString(next)));
            }
        } catch (Exception e) {
            return null;
        }
        return jsonObjectList;
    }

    public List<JSONObject> find(String collectionName, Map<String, Object> criteria_params, String sortKey, int sortBy) {
        List<JSONObject> jsonObjectList = new LinkedList<JSONObject>();
        try {
            BasicDBObject basicDBObject = new BasicDBObject();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                basicDBObject.append(key, obj);
            }

            FindIterable<Document> findIterable = mongoTemplate.getCollection(collectionName).find(basicDBObject).sort(new BasicDBObject(sortKey, sortBy));
            Iterator<Document> iteratorSet = findIterable.iterator();
            while (iteratorSet.hasNext()) {
                Document next = iteratorSet.next();
                jsonObjectList.add(JSON.parseObject(JSON.toJSONString(next)));
            }
        } catch (Exception e) {
            return null;
        }
        return jsonObjectList;
    }

    public List<JSONObject> find(String collectionName, Map<String, Object> criteria_params, String orderKey, int sortBy, int limits) {
        if (limits <= 0)
            limits = 0;

        List<JSONObject> jsonObjectList = new LinkedList<JSONObject>();
        try {
            BasicDBObject basicDBObject = new BasicDBObject();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                basicDBObject.append(key, obj);
            }
            FindIterable<Document> findIterable = mongoTemplate.getCollection(collectionName).find(basicDBObject).limit(limits).sort(new BasicDBObject(orderKey, sortBy));
            Iterator<Document> iteratorSet = findIterable.iterator();
            while (iteratorSet.hasNext()) {
                Document next = iteratorSet.next();
                jsonObjectList.add(JSON.parseObject(JSON.toJSONString(next)));
            }
        } catch (Exception e) {
            return null;
        }
        return jsonObjectList;
    }

    public List<JSONObject> find(String collectionName, Map<String, Object> criteria_params, String sortKey, int sortBy, int skipIdx, int limits) {

        if (skipIdx <= 0) {
            skipIdx = 0;
        }

        if (limits <= 0) {
            limits = 1;
        }

        List<JSONObject> jsonObjectList = new LinkedList<JSONObject>();
        try {
            BasicDBObject basicDBObject = new BasicDBObject();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                basicDBObject.append(key, obj);
            }
            FindIterable<Document> findIterable = mongoTemplate.getCollection(collectionName).find(basicDBObject).skip(skipIdx).limit(limits).sort(new BasicDBObject("timestamp", -1));
            Iterator<Document> iteratorSet = findIterable.iterator();
            while (iteratorSet.hasNext()) {
                Document next = iteratorSet.next();
                jsonObjectList.add(JSON.parseObject(JSON.toJSONString(next)));
            }
        } catch (Exception e) {
            return null;
        }
        return jsonObjectList;
    }

    public boolean save(String collectionName, JSONObject json) {
        try {
            mongoTemplate.save(json, collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean save(String collectionName, JSONArray jsonList) {
        try {
            mongoTemplate.save(jsonList, collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean save(String collectionName, Map<String, String> criteria_params, String update_json_key, JSONObject update_json) {
        try {
            List<Criteria> criterias = new ArrayList<Criteria>();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                criterias.add(Criteria.where(key).is(obj));
            }
            Criteria[] criteriasArr = new Criteria[criterias.size()];
            criterias.toArray(criteriasArr);
            mongoTemplate.upsert(new Query().addCriteria(new Criteria().andOperator(criteriasArr)), new Update().set(update_json_key, update_json), collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean save(String collectionName, String update_json_key, JSONObject update_json_value, Map<String, Object> criteria_params) {
        try {
            List<Criteria> criterias = new ArrayList<Criteria>();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                criterias.add(Criteria.where(key).is(obj));
            }
            Criteria[] criteriasArr = new Criteria[criterias.size()];
            criterias.toArray(criteriasArr);
            mongoTemplate.upsert(new Query().addCriteria(new Criteria().andOperator(criteriasArr)), new Update().addToSet(update_json_key, update_json_value), collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }


    public boolean save(String collectionName, Map<String, Object> criteria_params, String update_array_key, JSONArray update_array_value) {
        try {
            List<Criteria> criterias = new ArrayList<Criteria>();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                criterias.add(Criteria.where(key).is(obj));
            }
            Criteria[] criteriasArr = new Criteria[criterias.size()];
            criterias.toArray(criteriasArr);
            mongoTemplate.upsert(new Query().addCriteria(new Criteria().andOperator(criteriasArr)), new Update().set(update_array_key, update_array_value), collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean drop(String collectionName) {
        try {
            mongoTemplate.dropCollection(collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean remove(String collectionName, Map<String, Integer> criteria_params) {
        try {
            BasicDBObject basicDBObject = new BasicDBObject();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                basicDBObject.append(key, obj);
            }
            mongoTemplate.remove(basicDBObject, collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean remove(String delete_params_key, Map<String, Object> delete_params_value, Map<String, Object> criteria_params, String collectionName) {
        try {
            Document document = new Document();
            List<Criteria> criterias = new ArrayList<Criteria>();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                criterias.add(Criteria.where(key).is(obj));
            }
            for (String key : delete_params_value.keySet()) {
                Object obj = delete_params_value.get(key);
                document.append(key, obj);
            }
            Criteria[] criteriasArr = new Criteria[criterias.size()];
            criterias.toArray(criteriasArr);
            mongoTemplate.upsert(new Query().addCriteria(new Criteria().andOperator(criteriasArr)), new Update().pull(delete_params_key, document), collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean delArrayElement(String strArrayName, int idx, Map<String, Object> criteria_params, String collectionName) {
        try {
            Document document = new Document();
            List<Criteria> criterias = new ArrayList<Criteria>();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                criterias.add(Criteria.where(key).is(obj));
            }
//
//            String strKey = strArrayName+"."+idx;
//            document.append(strKey,"");
//            for (String key : delete_params_value.keySet()) {
//                Object obj = delete_params_value.get(key);
//
//            }
//
//            Criteria[] criteriasArr=new Criteria[criterias.size()];
//            criterias.toArray(criteriasArr);
//            mongoTemplate.upsert(new Query().addCriteria(new Criteria().andOperator(criteriasArr)),new Update().pull(delete_params_key, document),collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public long delete(Map<String, Object> criteria_params, String collectionName) {
        long ret = 0;
        try {
            List<Criteria> criterias = new ArrayList<Criteria>();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                criterias.add(Criteria.where(key).is(obj));
            }
            Criteria[] criteriasArr = new Criteria[criterias.size()];
            criterias.toArray(criteriasArr);
            ret = mongoTemplate.remove(new Query().addCriteria(new Criteria().andOperator(criteriasArr)), collectionName).getDeletedCount();

        } catch (Exception e) {

        }

        return ret;
    }


    public boolean remove(String collectionName, Query query) {
        try {
            mongoTemplate.remove(query, collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean exists(Map<String, Object> criteria_params, String collectionName) {
        try {
            List<Criteria> criterias = new ArrayList<Criteria>();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                criterias.add(Criteria.where(key).is(obj));
            }
            Criteria[] criteriasArr = new Criteria[criterias.size()];
            criterias.toArray(criteriasArr);
            return mongoTemplate.exists(new Query().addCriteria(new Criteria().andOperator(criteriasArr)), collectionName);
        } catch (Exception e) {
            return false;
        }
    }


    public boolean update(String collectionName, String criteria_key, Object criteria_param, String update_key, JSONObject update_json) {
        try {
            mongoTemplate.updateMulti(new Query().addCriteria(Criteria.where(criteria_key).is(criteria_param)), new Update().set(update_key, update_json), collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean update(String collectionName, String criteria_key, Object criteria_param, String update_key, JSONArray update_json) {
        try {
            mongoTemplate.updateMulti(new Query().addCriteria(Criteria.where(criteria_key).is(criteria_param)), new Update().set(update_key, update_json), collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean update(String collectionName, String criteria_key, Object criteria_param, String update_key, boolean update_value) {
        try {
            mongoTemplate.updateMulti(new Query().addCriteria(Criteria.where(criteria_key).is(criteria_param)), new Update().set(update_key, update_value), collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean update(String collectionName, String criteria_key, Object criteria_param, String update_key, int update_value) {
        try {
            mongoTemplate.updateMulti(new Query().addCriteria(Criteria.where(criteria_key).is(criteria_param)), new Update().set(update_key, update_value), collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean update(String collectionName, String criteria_key, Object criteria_param, String update_key, double update_value) {
        try {
            mongoTemplate.updateMulti(new Query().addCriteria(Criteria.where(criteria_key).is(criteria_param)), new Update().set(update_key, update_value), collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean update(String collectionName, Map<String, Object> criteria_params, String update_key, String update_str) {
        try {
            List<Criteria> criterias = new ArrayList<Criteria>();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                criterias.add(Criteria.where(key).is(obj));
            }
            Criteria[] criteriasArr = new Criteria[criterias.size()];
            criterias.toArray(criteriasArr);
            mongoTemplate.updateMulti(new Query().addCriteria(new Criteria().andOperator(criteriasArr)), new Update().set(update_key, update_str), collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean update(String collectionName, Map<String, Object> criteria_params, String update_key, boolean update_value) {
        try {
            List<Criteria> criterias = new ArrayList<Criteria>();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                criterias.add(Criteria.where(key).is(obj));
            }
            Criteria[] criteriasArr = new Criteria[criterias.size()];
            criterias.toArray(criteriasArr);
            mongoTemplate.updateMulti(new Query().addCriteria(new Criteria().andOperator(criteriasArr)), new Update().set(update_key, update_value), collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean update(String collectionName, Map<String, Object> criteria_params, String update_key, int update_value) {
        try {
            List<Criteria> criterias = new ArrayList<Criteria>();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                criterias.add(Criteria.where(key).is(obj));
            }
            Criteria[] criteriasArr = new Criteria[criterias.size()];
            criterias.toArray(criteriasArr);
            mongoTemplate.updateMulti(new Query().addCriteria(new Criteria().andOperator(criteriasArr)), new Update().set(update_key, update_value), collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean update(String collectionName, Map<String, Object> criteria_params, String update_key, float update_value) {
        try {
            List<Criteria> criterias = new ArrayList<Criteria>();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                criterias.add(Criteria.where(key).is(obj));
            }
            Criteria[] criteriasArr = new Criteria[criterias.size()];
            criterias.toArray(criteriasArr);
            mongoTemplate.updateMulti(new Query().addCriteria(new Criteria().andOperator(criteriasArr)), new Update().set(update_key, update_value), collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }


    public boolean update(String collectionName, Map<String, Object> criteria_params, String update_key, JSONObject update_json) {
        try {
            List<Criteria> criterias = new ArrayList<Criteria>();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                criterias.add(Criteria.where(key).is(obj));
            }
            Criteria[] criteriasArr = new Criteria[criterias.size()];
            criterias.toArray(criteriasArr);
            mongoTemplate.updateMulti(new Query().addCriteria(new Criteria().andOperator(criteriasArr)), new Update().set(update_key, update_json), collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }


    public boolean update(String collectionName, Map<String, Object> criteria_params, String update_key, JSONArray update_jsonList) {
        try {
            List<Criteria> criterias = new ArrayList<Criteria>();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                criterias.add(Criteria.where(key).is(obj));
            }
            Criteria[] criteriasArr = new Criteria[criterias.size()];
            criterias.toArray(criteriasArr);
            mongoTemplate.updateMulti(new Query().addCriteria(new Criteria().andOperator(criteriasArr)), new Update().set(update_key, update_jsonList), collectionName);
            return true;
        } catch (Exception e) {
            return false;
        }
    }


    public boolean update(String collectionName, Map<String, Object> criteria_params, Map<String, Object> update_params) {
        try {
            List<Criteria> criterias = new ArrayList<Criteria>();
            for (String key : criteria_params.keySet()) {
                Object obj = criteria_params.get(key);
                criterias.add(Criteria.where(key).is(obj));
            }
            Criteria[] criteriasArr = new Criteria[criterias.size()];
            criterias.toArray(criteriasArr);
            for (String key : update_params.keySet()) {
                Object obj = update_params.get(key);
                mongoTemplate.updateMulti(new Query().addCriteria(new Criteria().andOperator(criteriasArr)), new Update().set(key, obj), collectionName);
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }

}
